package com.base.monitor.module.biz.context;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.base.monitor.model.model.biz.collect.entity.Blacklist;
import com.base.monitor.module.biz.util.ChannelUtils;
import io.netty.channel.ChannelHandlerContext;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 客户端信息上下文
 */
@Slf4j
public class ClientContext {

    // 客户端注册表，格式：Map<command, ChannelInfo>
    private static final Map<String, ChannelInfo> clientMap = new ConcurrentHashMap<>();

    // 客户端版本信息Map<command，版本号>
    private static final Map<String, String> versionMap = new ConcurrentHashMap<>();

    // 客户端下发指令成功的map，格式为<command，随意内容>，只要值不是null就说明下发成功
    private static final Map<String, String> sendCollectMap = new ConcurrentHashMap<>();

    // 黑名单，黑名单中存储的是连接命令
    private static final List<Blacklist> blacklist = new CopyOnWriteArrayList<>();

    /**
     * 信道信息
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ChannelInfo {
        private String id;
        private String ip;
        private String gatewayId;
        private String command;
        private Date connectTime;
        private ChannelHandlerContext ctx;

        public ChannelInfo(String message, ChannelHandlerContext ctx) {
            this.id = ctx.channel().id().asShortText();
            this.ip = ChannelUtils.formatChannelIp(ctx.channel().remoteAddress().toString());
            this.gatewayId = message.substring(4);
            this.command = message;
            this.connectTime = new Date();
            this.ctx = ctx;
        }
    }

    /**
     * 新增客户端
     * @param message 连接信息
     * @param ctx 信道信息
     */
    public static void addClient(String message, ChannelHandlerContext ctx) {
        // 1.创建信道资料
        ChannelInfo channelInfo = new ChannelInfo(message, ctx);

        // 2.新增
        clientMap.put(channelInfo.getCommand(), channelInfo);
    }

    /**
     * 获取所有客户端信息
     * @return
     */
    public static Map<String, ChannelInfo> getClientMap() {
        return clientMap;
    }

    /**
     * 获取所有的信道列表
     * @return
     */
    public static List<ChannelInfo> getChannelList() {
        return new ArrayList<>(clientMap.values());
    }

    /**
     * 根据ip地址获取信道信息
     * @param ip ip地址
     * @return
     */
    public static ChannelInfo getChannelInfoByIp(String ip) {
        if (CollectionUtil.isEmpty(clientMap) || StrUtil.isBlank(ip)) {
            return null;
        }

        ip = ChannelUtils.formatChannelIp(ip);
        for (Map.Entry<String, ChannelInfo> entry : clientMap.entrySet()) {
            ChannelInfo channelInfo = entry.getValue();
            if (channelInfo == null) {
                continue;
            }
            if (StrUtil.isBlank(channelInfo.getIp())) {
                continue;
            }
            if (channelInfo.getIp().equals(ip)) {
                return channelInfo;
            }
        }
        return null;
    }

    /**
     * 根据信道id获取信道信息
     * @param id 信道id
     * @return
     */
    public static ChannelInfo getChannelInfoById(String id) {
        if (CollectionUtil.isEmpty(clientMap) || StrUtil.isBlank(id)) {
            return null;
        }
        for (Map.Entry<String, ChannelInfo> entry : clientMap.entrySet()) {
            ChannelInfo channelInfo = entry.getValue();
            if (channelInfo == null) {
                continue;
            }
            if (StrUtil.isBlank(channelInfo.getId())) {
                continue;
            }
            if (channelInfo.getId().equals(id)) {
                return channelInfo;
            }
        }
        return null;
    }

    /**
     * 根据连接命令获取信道信息
     * @param command 连接命令
     * @return
     */
    public static ChannelInfo getChannelInfoByCommand(String command) {
        if (CollectionUtil.isEmpty(clientMap) || StrUtil.isBlank(command)) {
            return null;
        }
        return clientMap.get(command);
    }

    /**
     * 根据连接命令获取信道上下文
     * @param command 连接命令
     * @return
     */
    public static ChannelHandlerContext getChannelHandlerContextByCommand(String command) {
        if (CollectionUtil.isEmpty(clientMap) || StrUtil.isBlank(command)) {
            return null;
        }
        ChannelInfo channelInfo = clientMap.get(command);
        if (channelInfo == null) {
            return null;
        }
        return channelInfo.getCtx();
    }

    /**
     * 获取所有的通道信息
     * @return
     */
    public static List<ChannelHandlerContext> getAllChannels() {
        if (CollectionUtil.isEmpty(clientMap)) {
            return null;
        }
        return new ArrayList<>(clientMap.values()).stream()
                .map(ChannelInfo::getCtx).collect(Collectors.toList());
    }

    /**
     * 根据ip地址获取对应信道的连接命令
     * @param ip ip地址
     * @return
     */
    public static String getCommandByIp(String ip) {
        ip = ChannelUtils.formatChannelIp(ip);
        ChannelInfo channelInfo = getChannelInfoByIp(ip);
        if (channelInfo == null) {
            return null;
        }
        return channelInfo.getCommand();
    }

    /**
     * 删除客户端
     * @param ctx 需要删除的通道信息
     */
    public static void removeClient(ChannelHandlerContext ctx) {
        // 1.根据信道上下文ctx从clientMap中找到需要删除的信道信息
        ChannelInfo channelInfo = getChannelInfoById(ctx.channel().id().asShortText());
        if (channelInfo == null) {
            return;
        }

        // 2.获取连接命令
        String command = channelInfo.getCommand();
        if (!clientMap.containsKey(command)) {
            return;
        }

        // 3.断开客户端连接
        ctx.close();

        // 4.从map中删除
        clientMap.remove(command);
    }

    /**
     * 清空所有客户端
     */
    public static void removeAllClient() {
        clientMap.clear();
    }

    /**
     * 添加客户端的版本信息
     * @param ip 客户端ip
     * @param version 客户端版本
     */
    public static void addClientVersion(String ip, String version) {
        if (StrUtil.isBlank(ip) || StrUtil.isBlank(version)) {
            return;
        }
        ip = ChannelUtils.formatChannelIp(ip);

        versionMap.put(getCommandByIp(ip), version);
    }

    /**
     * 根据信道列表，查询他们的客户端版本
     * @param list 信道列表
     * @return
     */
    public static Map<String, String> getClientVersion(List<ChannelHandlerContext> list) {
        Map<String, String> map = new HashMap<>();
        if (CollectionUtil.isEmpty(list)) {
            return map;
        }
        List<String> commands = list.stream().map(item -> {
            String ip = item.channel().remoteAddress().toString();
            ip = ChannelUtils.formatChannelIp(ip);
            return getCommandByIp(ip);
        }).collect(Collectors.toList());
        for (String command : commands) {
            if (versionMap.containsKey(command)) {
                map.put(command, versionMap.get(command));
            }
        }
        return map;
    }

    /**
     * 获取版本信息
     * @return
     */
    public static Map<String, String> getVersionMap() {
        return versionMap;
    }

    /**
     * 清空版本信息
     */
    public static void clearClientVersion() {
        versionMap.clear();
    }

    /**
     * 添加客户端的下发结果信息
     * @param ip 客户端ip
     * @param message 信息，随意，不是null就说明下发成功
     */
    public static void addSendCollect(String ip, String message) {
        if (StrUtil.isBlank(ip)) {
            return;
        }

        ip = ChannelUtils.formatChannelIp(ip);
        sendCollectMap.put(getCommandByIp(ip), message);
    }

    /**
     * 根据信道列表，查询他们的客户端下发结果
     * @param list 信道列表
     * @return
     */
    public static Map<String, String> getSendCollect(List<ChannelHandlerContext> list) {
        Map<String, String> map = new HashMap<>();
        if (CollectionUtil.isEmpty(list)) {
            return map;
        }
        List<String> commands = list.stream().map(item -> {
            String ip = item.channel().remoteAddress().toString();
            ip = ChannelUtils.formatChannelIp(ip);
            return getCommandByIp(ip);
        }).collect(Collectors.toList());
        for (String command : commands) {
            if (sendCollectMap.containsKey(command)) {
                map.put(command, sendCollectMap.get(command));
            }
        }
        return map;
    }

    /**
     * 获取版本信息
     * @return
     */
    public static Map<String, String> getSendCollectMap() {
        return sendCollectMap;
    }

    /**
     * 清空版本信息
     */
    public static void clearSendCollectMap() {
        sendCollectMap.clear();
    }

    /**
     * 根据信道信息从sendCollectMap中删除指定ip的记录
     * @param ctx 信道信息
     */
    public static void removeSendClient(ChannelHandlerContext ctx) {
        // 1.根据信道上下文ctx从clientMap中找到需要删除的信道信息
        String ip = ChannelUtils.formatChannelIp(ctx.channel().remoteAddress().toString());

        String command = getCommandByIp(ip);
        if (!sendCollectMap.containsKey(command)) {
            return;
        }
        sendCollectMap.remove(command);
    }

    /**
     * 添加黑名单集合
     * @param blacks 黑名单集合
     */
    public static void setBlacklist(List<Blacklist> blacks) {
        blacklist.addAll(blacks);
    }

    /**
     * 添加单个黑名单
     * @param black 黑名单
     */
    public static void addBlacklist(Blacklist black) {
        blacklist.add(black);
    }

    /**
     * 获取黑名单集合
     * @return
     */
    public static List<Blacklist> getBlacklist() {
        return blacklist;
    }

    /**
     * 判断该信道是否能连接
     * @param command 信道对应的连接命令
     * @return
     */
    public static Boolean canConnect(String command) {
        List<String> collect = blacklist.stream().map(Blacklist::getCommand).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(collect)) {
            return true;
        }
        return !collect.contains(command);
    }

    /**
     * 删除信道黑名单
     * @param command 信道对应的连接命令
     */
    public static void removeBlacklist(String command) {
        if (CollectionUtil.isEmpty(blacklist)) {
            return;
        }
        for (Blacklist black : blacklist) {
            if (black.getCommand().equals(command)) {
                blacklist.remove(black);
                break;
            }
        }

    }

    /**
     * 清空黑名单
     */
    public static void clearBlacklist() {
        blacklist.clear();
    }

}
